/*
 * MIT License
 *
 * Copyright (c) 2024-2048 冰羽
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.star.framework.extend.entity;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.star.framework.extend.core.Database;
import cn.star.framework.sql.Identifier;
import cn.star.framework.sql.standard.Sorter;
import cn.star.framework.sql.standard.Where;
import cn.star.framework.sql.standard.WhereGroup;
import cn.star.framework.type.OrderType;
import cn.star.framework.type.QueryType;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;

/**
 * EntityWrapper
 *
 * @author zhaoweiping
 *     <p style='color: red'>Created on 2024-09-23 11:06:00
 * @since 3.0.0
 */
public class EntityWrapper implements AutoCloseable {

  /** 数据库 */
  @Getter private final Database database;

  @Getter @Setter private String schema;

  /** 表名 */
  @Getter private final String tableName;

  /**
   * 实体 类型<br>
   * 非必需参数
   */
  @Getter private final Class<?> type;

  /**
   * 数据 集合<br>
   * 新增、更新（推荐一条）、删除（推荐一条）
   */
  @Getter @Setter private List<?> data;

  /** 查询 条件 */
  @Getter @Setter private List<Where> wheres = new ArrayList<>();

  /**
   * 查询 组条件<br>
   * 条件分组的顺序添加，最后根据{@link WhereGroup#getOperator()}连接，即<span style='color: red'>and | or ({@link
   * WhereGroup})</span>
   */
  @Getter @Setter private List<WhereGroup> whereGroups = new ArrayList<>();

  /** 查询 排序 */
  @Getter @Setter private List<Sorter> sorters = new ArrayList<>();

  /** current session */
  private Session session;

  /**
   * 需要查询返回的列字段<br>
   * 默认查询返回所有的字段列表，当然若有实体类型{@link #type}，实体类型注解{@link JsonProperty#access()}值为{@link
   * JsonProperty.Access#WRITE_ONLY}会生效不返回给查询端
   */
  private final Map<String, String> queryColumnNameAliasMap = new LinkedHashMap<>();

  public EntityWrapper(Database database, String tableName) {
    this(database, tableName, null);
  }

  public EntityWrapper(Database database, String tableName, Class<?> type) {
    this.database = database;
    this.tableName = tableName;
    this.type = type;
  }

  /**
   * 条件查询条件
   *
   * @param columnName 数据库表列名(支持实体字段名)
   * @param type {@link QueryType}
   */
  public EntityWrapper addWhere(String columnName, QueryType type) {
    this.wheres.add(new Where(new Identifier(columnName), type, null));

    return this;
  }

  /**
   * 条件查询条件
   *
   * @param columnName 数据库表列名(支持实体字段名)
   * @param type {@link QueryType}
   * @param value 条件值
   */
  public EntityWrapper addWhere(String columnName, QueryType type, Object value) {
    this.wheres.add(new Where(new Identifier(columnName), type, value));

    return this;
  }

  /**
   * 设置查询排序规则
   *
   * @param type {@link OrderType}
   * @param columnNames 数据库表列(支持实体字段名)
   */
  public EntityWrapper addSorterColumnNames(OrderType type, String... columnNames) {
    if (ArrayUtil.isNotEmpty(columnNames)) {
      List<Identifier> names =
          CollectionUtil.toList(columnNames).stream()
              .map(Identifier::new)
              .collect(Collectors.toList());
      this.sorters.add(new Sorter(type, names));
    }

    return this;
  }

  /**
   * 定制查询返回的列
   *
   * @param columnName 数据库表列名(不支持实体字段名)
   */
  public EntityWrapper addQueryColumnName(String columnName) {
    return this.addQueryColumnName(columnName, columnName);
  }

  /**
   * 定制查询返回的列
   *
   * @param columnName 数据库表列名(不支持实体字段名)
   * @param alias 别名
   */
  public EntityWrapper addQueryColumnName(String columnName, String alias) {
    this.queryColumnNameAliasMap.put(columnName, alias);

    return this;
  }

  /** 获取查询列名 */
  public List<String> getQueryColumnNames() {
    Set<String> keys = queryColumnNameAliasMap.keySet();

    return keys.isEmpty() ? new ArrayList<>() : new ArrayList<>(keys);
  }

  /**
   * 获取列名别名
   *
   * @param columnName 数据库表列名(不支持实体字段名)
   */
  public String getQueryColumnNameAlias(String columnName) {
    // 不区分大小写
    String key =
        queryColumnNameAliasMap.keySet().stream()
            .filter(i -> i.equalsIgnoreCase(columnName))
            .findFirst()
            .orElse(columnName);
    String name = queryColumnNameAliasMap.get(key);

    return name == null ? columnName : name;
  }

  public Session getSession() {
    if (session == null || ((SessionImpl) session).isClosed()) {
      this.session = database.getSession();
    }

    return session;
  }

  @Override
  public void close() throws Exception {
    try {
      this.session.close();
    } catch (Exception e) {
      // 忽略异常
    }
  }

  @Override
  protected void finalize() throws Throwable {
    this.close();
    super.finalize();
  }
}
